import axios from 'axios';

import type {
    AxiosInstance,
    AxiosRequestConfig,
    AxiosResponse,
    InternalAxiosRequestConfig,
    AxiosError,
} from 'axios';

type Result<T> = {
    code: number;
    message: string;
    result: T;
};

// 导出Request类，可以用来自定义传递配置来创建实例
export class Request {
    // axios 实例
    instance: AxiosInstance;
    // 基础配置，url和超时时间
    baseConfig: AxiosRequestConfig = {
        baseURL: '/hotel', //请求后台地址模块
        timeout: 60000,
        headers: {
            'X-Requested-With': 'XMLHttpRequest',
        },
    };

    constructor(config: AxiosRequestConfig) {
        // 使用axios.create创建axios实例
        this.instance = axios.create(Object.assign(this.baseConfig, config));

        // 请求拦截器
        this.instance.interceptors.request.use(
            (config: InternalAxiosRequestConfig) => {
                // 一般会请求拦截里面加token，用于后端的验证
                const token = localStorage.getItem('token') as string;
                if (token) {
                    config.headers!.Authorization = token;
                }

                return config;
            },
            (err: unknown) => {
                // 请求错误，这里可以用全局提示框进行提示
                return Promise.reject(err);
            }
        );

        // 返回结果的相应拦截器
        this.instance.interceptors.response.use(
            (res: AxiosResponse) => {
                // 直接返回res，当然你也可以只返回res.data
                // 系统如果有自定义code也可以在这里处理
                return res.data;
            },
            (err: AxiosError) => {
                const { response } = err;

                // 这里用来处理http常见错误，进行全局提示
                if (response) {
                    let message = '';

                    switch (response.status) {
                        case 400:
                            message = '请求错误(400)';
                            break;
                        case 401:
                            message = '未授权，请重新登录(401)';
                            // 这里可以做清空storage并跳转到登录页的操作
                            break;
                        case 403:
                            message = '拒绝访问(403)';
                            break;
                        case 404:
                            message = '请求出错(404)';
                            break;
                        case 408:
                            message = '请求超时(408)';
                            break;
                        case 500:
                            message = '服务器错误(500)';
                            break;
                        case 501:
                            message = '服务未实现(501)';
                            break;
                        case 502:
                            message = '网络错误(502)';
                            break;
                        case 503:
                            message = '服务不可用(503)';
                            break;
                        case 504:
                            message = '网络超时(504)';
                            break;
                        case 505:
                            message = 'HTTP版本不受支持(505)';
                            break;
                        default:
                            message = `连接出错(${response.status})!`;
                    }

                    console.error(message);
                }
                // 这里错误消息可以使用全局弹框展示出来
                // 比如element plus 可以使用 ElMessage
                // ElMessage({
                //   showClose: true,
                //   message: `${message}，请检查网络或联系管理员！`,
                //   type: "error",
                // });
                // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
                return Promise.reject(response);
            }
        );
    }

    // 定义请求方法
    public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.instance.request(config);
    }

    public get<T = unknown>(
        url: string,
        config?: AxiosRequestConfig
    ): Promise<AxiosResponse<Result<T>>> {
        return this.instance.get(url, config);
    }

    public post<T = unknown>(
        url: string,
        data?: Record<string, unknown>,
        config?: AxiosRequestConfig
    ): Promise<AxiosResponse<Result<T>>> {
        return this.instance.post(url, data, config);
    }

    public upload<T = unknown>(
        url: string,
        data?: Record<string, unknown>,
        config?: AxiosRequestConfig
    ): Promise<AxiosResponse<Result<T>>> {
        //封装表单数据对象
        const RequestData = new FormData();

        // 检查传入的 data 是否存在
        if (!data || Object.getOwnPropertyNames(data).length === 0) {
            throw new Error('No data provided for upload');
        }

        // 遍历参数并添加到 RequestData 中
        Object.entries(data).forEach(([key, value]) => {
            if (Array.isArray(value)) {
                // 如果值是数组，遍历数组中的每个元素
                value.forEach((item) => {
                    if (item instanceof File) {
                        // 如果是文件
                        RequestData.append(`${key}[]`, item);
                    } else if (typeof item === 'string') {
                        // 如果是字符串或数字
                        RequestData.append(`${key}[]`, item);
                    }else if (typeof item === 'number') {
                        // 如果是字符串或数字
                        RequestData.append(`${key}[]`, item.toString());
                    } else {
                        throw new Error(`Unsupported array item type for key "${key}"`);
                    }
                });
            } else if (value instanceof File) {
                // 如果是单个文件
                RequestData.append(key, value);
            } else if (typeof value === 'string' || typeof value === 'number') {
                // 如果是字符串或数字
                RequestData.append(key, value.toString());
            } else {
                throw new Error(`Unsupported data type for key "${key}"`);
            }
        });

        // 请求头
        config = {
            ...config,
            headers: {
                ...config?.headers,
                'Content-Type': 'multipart/form-data',
            },
        };

        return this.instance.post(url, RequestData, config);
    }

    public put<T = object>(
        url: string,
        data?: Record<string, unknown>,
        config?: AxiosRequestConfig
    ): Promise<AxiosResponse<Result<T>>> {
        return this.instance.put(url, data, config);
    }

    public delete<T = unknown>(
        url: string,
        config?: AxiosRequestConfig
    ): Promise<AxiosResponse<Result<T>>> {
        return this.instance.delete(url, config);
    }
}

// 默认导出Request实例
export default new Request({});
